package j.util.pkgenerator;

import j.util.DateUtils;
import j.util.StringUtils;
import j.util.jdbc.JdbcUtils;

import javax.sql.DataSource;
import java.sql.*;
import java.sql.Date;
import java.util.*;

/**
 * 默认的主键生成器实现接口，如果采用默认的，必须做到以下几点:
 *  1.该项目的数据库中有表，且表结构必须是：sys_table_pk_generator（table_key,prefix,gen_value,allocation_size,recounting_by_day,last_retrive_date）
 *    且gen_value,allocation_size,recounting_by_day为int类型，last_retrive_date为日期类型（仅保定日期即可）
 *  2.在listener初始化生成器时，需要指定数据源
 * User: jason
 * Date: 13-4-22 上午10:35
 */
public class DefaultPkGeneratorServiceImpl implements PkGeneratorService {
    private DataSource dataSource;

    public DefaultPkGeneratorServiceImpl(DataSource dataSource){
        this.dataSource = dataSource;
    }

    /**
     * 根据数据库中的配置产生下一个序列，并考虑是否隔天重新计数
     * @param key
     * @return
     */
    private NextValue _generateNextValueByKey(String key){
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        boolean b = false;
        try{
            conn = dataSource.getConnection();
            conn.setAutoCommit(false);
            conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
            stmt = conn.prepareStatement("SELECT prefix,gen_value,allocation_size,recounting_by_day,last_retrive_date,length,date_format FROM sys_table_pk_generator where table_key=?");
            stmt.setString(1,key);
            rs = stmt.executeQuery();
            if(rs.next()){
                NextValue nv = new NextValue();
                nv.setPrefix(rs.getString("prefix"));
                nv.setBeginIndex(rs.getInt("gen_value"));
                nv.setAllocationSize(rs.getInt("allocation_size"));
                nv.setNextMaxIndex(nv.getBeginIndex() + nv.getAllocationSize());
                nv.setRecountingByDay(rs.getBoolean("recounting_by_day"));
                nv.setLastRetriveDate(rs.getDate("last_retrive_date"));
                nv.setLength(rs.getInt("length"));
                nv.setDateFormat(rs.getString("date_format"));

                if(nv.isRecountingByDay()){
                    java.util.Date now = new java.util.Date();
                    if(!DateUtils.isSameDay(nv.getLastRetriveDate(), now)){
                        //上次获取时间与当前不是同一天了，需要重置为0
                        b = true;
                        nv.setLastRetriveDate(now);
                        nv.setBeginIndex(0);
                        nv.setNextMaxIndex(nv.getAllocationSize());
                        stmt = conn.prepareStatement("UPDATE sys_table_pk_generator SET gen_value=allocation_size,last_retrive_date=? WHERE table_key=?");
                        stmt.setDate(1,new Date(now.getTime()));
                        //stmt.setDate(1,new Date(System.currentTimeMillis()));
                        stmt.setString(2,key);
                    }
                }
                if(!b){
                    stmt = conn.prepareStatement("UPDATE sys_table_pk_generator SET gen_value=gen_value+allocation_size WHERE table_key=?");
                    stmt.setString(1,key);
                }
                stmt.executeUpdate();
                conn.commit();
                return nv;
            }else{
                //当数据库中不存在时，按此key建立一个
                NextValue nv = new NextValue();
                nv.setPrefix(null);
                nv.setBeginIndex(1);
                nv.setAllocationSize(1);
                nv.setNextMaxIndex(nv.getBeginIndex() + nv.getAllocationSize());
                nv.setRecountingByDay(true);
                nv.setLastRetriveDate(new java.util.Date());
                nv.setLength(20);
                nv.setDateFormat("yyyyMMdd");
                stmt = conn.prepareStatement("insert into sys_table_pk_generator (table_key,table_name,table_c_name,gen_value,allocation_size,recounting_by_day,last_retrive_date,length,date_format)values(?,?,?,2,1,1,?,20,'yyyyMMdd')");
                stmt.setString(1,key);
                stmt.setString(2,key);
                stmt.setString(3,key);
                stmt.setTimestamp(4, new Timestamp(new java.util.Date().getTime()));
                stmt.executeUpdate();
                conn.commit();
                return nv;
            }
        }catch(SQLException e){
            e.printStackTrace();
            try {
                if(null!=conn){
                    conn.rollback();
                }
            } catch (SQLException ignored) {}
        }finally{
            JdbcUtils.free(conn, stmt, rs);
        }
        return null;
    }

    /**
     *
     * @param key 配置文件中的唯一值
     * @return
     */
    public NextValue generateNextValueByKey(String key) {
        NextValue nv = _generateNextValueByKey(key);
        int i=1;
        while(nv == null && i++<=5){
            nv = _generateNextValueByKey(key);
        }
        if(nv == null){
            throw new RuntimeException("由于系统繁忙，在生成主键时失败，请稍后再试！");
        }
        return nv;
    }

    public int generatePrimaryKeyForInt(String key) {
        return 0;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public String generatePrimaryKey(String prifix,int length,String dateFormat,int index){
        String pk = "";
        if(StringUtils.isNotEmpty(prifix)){
            pk = prifix;
        }
        if(StringUtils.isNotEmpty(dateFormat)){
            //在pk上增加年月日
            pk += DateUtils.format(new java.util.Date(), dateFormat);
        }
        String seq = String.valueOf(index);
        seq = StringUtils.leftPad(seq, length - pk.length(), "0");
        pk = pk + seq;
        return pk;
    }

    public int getAndUpdateGenValue(String key,java.util.Date lastRetriveDate) {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        int cValue = 1;
        try{
            conn = dataSource.getConnection();
            conn.setAutoCommit(false);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            stmt = conn.prepareStatement("select gen_value from sys_table_pk_generator where table_key=?");
            stmt.setString(1,key);
            rs = stmt.executeQuery();
            if(rs.next()){
                cValue = rs.getInt(1);
            }
            rs.close();
            stmt.close();
            stmt = conn.prepareStatement("UPDATE sys_table_pk_generator SET gen_value=gen_value+?,last_retrive_date=? WHERE table_key=?");
            stmt.setInt(1,cValue);
            stmt.setDate(2,new Date(lastRetriveDate.getTime()));
            stmt.setString(3,key);
            stmt.executeUpdate();
            conn.commit();
        }catch(SQLException e){
            if(conn != null){
                try {
                    conn.rollback();
                } catch (SQLException ignored) {}
            }
            e.printStackTrace();
        }finally{
            JdbcUtils.free(conn,stmt,rs);
        }
        return cValue;
    }
}
